package cn.edu.swu.mhans.game.listener;

import cn.edu.swu.mhans.common.player.NegotiationCommand;
import cn.edu.swu.mhans.common.player.NegotiationPlayer;
import cn.edu.swu.mhans.common.player.WebSocketHumanNegotiationPlayer;
import cn.edu.swu.mhans.common.util.OfferUtils;
import cn.edu.swu.mhans.game.constants.NormalCommandType;
import cn.edu.swu.mhans.game.model.ActionLog;
import cn.edu.swu.mhans.game.model.NegotiationResult;
import cn.edu.swu.mhans.game.service.ActionLogService;
import cn.edu.swu.mhans.game.service.NegotiationResultService;
import cn.edu.swu.mhans.room.demo.NormalRoom;
import cn.edu.swu.mhans.room.service.RoomService;
import cn.edu.swu.mhans.room.share.base.BaseRoom;
import cn.edu.swu.mhans.user.model.SystemUser;
import cn.edu.swu.mhans.user.service.UserService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;

/**
 * @author ZhanJingbo
 * @version 1.0.0
 * Created on 2018/4/28
 */
@Component
@Slf4j
public class NormalGameListener {

    /**
     * 消息队列 队列名称
     */
    private static final String QUEUE_NAME = "normalGame";

    @Autowired
    private RoomService roomService;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private NegotiationResultService negotiationResultService;
    @Autowired
    private ActionLogService actionLogService;
    @Autowired
    private UserService userService;

    @RabbitListener(queues = "normalGame")
    @RabbitHandler
    public void handler(String jsonCommand) {
        log.info("消息消费:{}", jsonCommand);
        ObjectMapper mapper = new ObjectMapper();
        NegotiationCommand command = null;
        try {
            command = mapper.readValue(jsonCommand, NegotiationCommand.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        NormalCommandType commandType = NormalCommandType.getCommandType(command.getCommandType());
        if (null == commandType) {
            return;
        }
        // 命令处理
        switch (commandType) {
            case CHAT_MESSAGE:
                handleChatMessage(command);
                break;
            case GAME_START:
                handleGameStart(command);
                break;
            case CHOOSE_MC:
                handleChooseMc(command);
                break;
            case MC_OFFER:
                handleMcOffer(command);
                break;
            case PLAYER_VOTE:
                handlePlayerVote(command);
                break;
            case GAME_OVER:
                handleGameOver(command);
                break;
            case BREAK_OFF:
                handleBreakOff(command);
                break;
            case INTO_ROOM:
                handleIntoRoom(command);
            default:
                break;
        }
        saveActionLog(command, jsonCommand);
    }

    /**
     * 处理玩家进入房间
     *
     * @param command
     */
    private synchronized void handleIntoRoom(NegotiationCommand command) {
        Long playerId = Long.parseLong(command.getData().get("playerId").toString());
        String roomId = command.getRoomId();
        SystemUser user = userService.findOne(playerId);

        WebSocketHumanNegotiationPlayer player = new WebSocketHumanNegotiationPlayer(user, roomId);
        BaseRoom room = roomService.findOne(roomId);
        if (null == room.getNegotiationPlayers()) {
            room.setNegotiationPlayers(new ArrayList<>());
        }
        if (room.getNegotiationPlayers().size() < room.getTotalPlayerCount()) {
            room.getNegotiationPlayers().add(player);
        }
        if (room.getNegotiationPlayers().size() == room.getTotalPlayerCount()) {
            //人数达标 游戏开始
            NegotiationCommand startCommand = new NegotiationCommand(NormalCommandType.GAME_START.getValue(), roomId);
            sendNegotiationCommand(startCommand);
        }
        roomService.add(room);
    }

    /**
     * 游戏异常结束
     *
     * @param command
     */
    private void handleBreakOff(NegotiationCommand command) {
        BaseRoom room = roomService.findOne(command.getRoomId());
        if (null == room) {
            return;
        }
        NegotiationCommand sentCommand = new NegotiationCommand(NormalCommandType.GAME_OVER.getValue(), room.getRoomId());
        Map<String, Object> data = Maps.newHashMap();
        data.put("totalValue", 0);
        data.put("score", 0);
        data.put("flag", "异常结束");
        sentCommand.setData(data);
        for (NegotiationPlayer player : room.getNegotiationPlayers()) {
            try {
                player.handleNegotiationCommand(sentCommand);
                saveNegotiationResult(room, player, data);
            } catch (Exception e) {
                continue;
            }
        }
        roomService.delete(room.getRoomId());
    }

    /**
     * 聊天消息处理
     */
    private void handleChatMessage(NegotiationCommand command) {
        BaseRoom room = roomService.findOne(command.getRoomId());
        String fromPlayerId = command.getFromNegotiationPlayerId();

        for (NegotiationPlayer player : room.getNegotiationPlayers()) {
            String id = player.getNegotiationPlayerId();
            if (id.equals(fromPlayerId)) {
                continue;
            }
            // 向房间中的其它玩家发送消息
            player.handleNegotiationCommand(command);
        }
    }

    /**
     * 游戏初始化
     */
    private void handleGameStart(NegotiationCommand command) {
        BaseRoom room = roomService.findOne(command.getRoomId());
        NormalRoom normalRoom = null;
        if (room instanceof NormalRoom) {
            normalRoom = (NormalRoom) room;
        }
        Collections.shuffle(normalRoom.getPlayerPreferencesList());
        List<Map<String, String>> playerList = Lists.newArrayList();
        for (int i = 0; i < room.getNegotiationPlayers().size(); i++) {
            NegotiationPlayer player = room.getNegotiationPlayers().get(i);
            player.setPlayerPreferences(normalRoom.getPlayerPreferencesList().get(i));

            Map<String, String> playerMap = Maps.newHashMap();
            playerMap.put("name", player.getNegotiationPlayerName());
            playerMap.put("type", player.getNegotiationPlayerType() == 0 ? "Agent" : "自然人");
            playerList.add(playerMap);
        }


        Map<String, Object> data = Maps.newHashMap();
        data.put("issueList", normalRoom.getIssueList());
        data.put("playerList", playerList);

        for (NegotiationPlayer player : room.getNegotiationPlayers()) {
            NegotiationCommand sendCommand = new NegotiationCommand(NormalCommandType.ALLOC_ROLE.getValue(), room.getRoomId());
            data.put("role", player.getPlayerPreference());
            sendCommand.setData(data);
            player.handleNegotiationCommand(sendCommand);
        }
        roomService.add(room);

        // 选举出价人
        NegotiationCommand chooseMc = new NegotiationCommand(NormalCommandType.CHOOSE_MC.getValue(), command.getRoomId());
        sendNegotiationCommand(chooseMc);
    }

    /**
     * 选择出价人
     */
    private void handleChooseMc(NegotiationCommand command) {
        NormalRoom room = (NormalRoom) roomService.findOne(command.getRoomId());
        // 投票状态重置
        room.setVoteStatus(new boolean[room.getTotalPlayerCount()]);
        room.setVoteCount(0);
        // 设置游戏状态
        room.setRoomStatus(1);

        // 选举出价人
        int current = room.getCurrentMc();
        current = (current + 1) % room.getTotalPlayerCount();
        room.setCurrentMc(current);
        roomService.add(room);

        NegotiationPlayer currentMc = room.getNegotiationPlayers().get(current);

        Map<String, Object> data = new HashMap<>();
        data.put("mcId", currentMc.getNegotiationPlayerId());
        data.put("mcName", currentMc.getNegotiationPlayerName());
        command.setData(data);

        for (NegotiationPlayer player : room.getNegotiationPlayers()) {
            player.handleNegotiationCommand(command);
        }

    }

    /**
     * 收到出价人的出价，要求各个玩家进行表决
     */
    private void handleMcOffer(NegotiationCommand command) {
        NormalRoom room = (NormalRoom) roomService.findOne(command.getRoomId());
        Map<String, Object> offerData = command.getData();

        NegotiationCommand voteCommand = new NegotiationCommand();
        voteCommand.setCommandType(NormalCommandType.VOTE.getValue());
        voteCommand.setRoomId(room.getRoomId());

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("offerPlayerName", room.getNegotiationPlayers().get(room.getCurrentMc()).getNegotiationPlayerName());
        dataMap.put("offerData", offerData);
        voteCommand.setData(dataMap);
        synchronized (room) {
            for (NegotiationPlayer player : room.getNegotiationPlayers()) {
                dataMap.put("score", OfferUtils.calcScore(offerData, player.getPlayerPreference()));
                player.handleNegotiationCommand(voteCommand);
            }
            room.setCurrentOfferData(offerData);
            roomService.add(room);
        }
    }

    /**
     * 处理玩家的投票结果
     */
    private void handlePlayerVote(NegotiationCommand command) {
        NormalRoom room = (NormalRoom) roomService.findOne(command.getRoomId());

        synchronized (room) {
            boolean voteResult = (boolean) command.getData().get("voteResult");
            NegotiationPlayer votePlayer = null;
            int index = 0;
            for (; index < room.getNegotiationPlayers().size(); index++) {
                if (room.getNegotiationPlayers().get(index).getNegotiationPlayerId().equals(command.getFromNegotiationPlayerId())) {
                    break;
                }
            }
            boolean[] voteStatus = room.getVoteStatus();
            voteStatus[index] = voteResult;
            room.setVoteCount(room.getVoteCount() + 1);
            roomService.add(room);
            if (room.getVoteCount() == room.getTotalPlayerCount()) {

                NegotiationCommand nextCommand = new NegotiationCommand();
                nextCommand.setRoomId(room.getRoomId());

                boolean vote = true;
                for (int i = 0; i < voteStatus.length; i++) {
                    if (!voteStatus[i]) {
                        vote = false;
                        break;
                    }
                }

                if (vote) {
                    nextCommand.setCommandType(NormalCommandType.GAME_OVER.getValue());
                } else {
                    nextCommand.setCommandType(NormalCommandType.CHOOSE_MC.getValue());
                }
                sendNegotiationCommand(nextCommand);
            }

        }
    }

    /**
     * 处理游戏结束
     */
    private void handleGameOver(NegotiationCommand command) {
        NormalRoom room = (NormalRoom) roomService.findOne(command.getRoomId());
        room.setRoomStatus(2);
        double totalValue = 0;
        for (NegotiationPlayer player : room.getNegotiationPlayers()) {
            totalValue += OfferUtils.calcScore(room.getCurrentOfferData(), player.getPlayerPreference());
        }
        Map<String, Object> data = Maps.newHashMap();
        data.put("totalValue", totalValue);
        data.put("finalOffer", room.getCurrentOfferData());
        for (NegotiationPlayer player : room.getNegotiationPlayers()) {
            data.put("score", OfferUtils.calcScore(room.getCurrentOfferData(), player.getPlayerPreference()));
            command.setData(data);
            player.handleNegotiationCommand(command);
            try {
                saveNegotiationResult(room, player, data);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                continue;
            }

        }
        roomService.delete(room.getRoomId());
    }

    /**
     * 向消息队列发送消息，推动游戏进程
     *
     * @param command
     */
    private void sendNegotiationCommand(NegotiationCommand command) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            amqpTemplate.convertAndSend(QUEUE_NAME, mapper.writeValueAsString(command));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

    private NegotiationResult createNegotiationResult(BaseRoom room, NegotiationPlayer player, String result) {
        NegotiationResult negotiationResult = new NegotiationResult();
        //填充玩家信息
        negotiationResult.setUserType(player.getNegotiationPlayerType());
        negotiationResult.setUserId(player.getNegotiationPlayerId());
        //填充房间信息
        negotiationResult.setRoomConfigId(room.getRoomConfigId());
        negotiationResult.setRoomName(room.getName());
        //填充结果信息
        negotiationResult.setNegotiationResult(result);
        //填充时间信息
        Date now = new Date();
        negotiationResult.setCreateTime(now);
        negotiationResult.setModifyTime(now);

        return negotiationResult;
    }

    private void saveActionLog(NegotiationCommand negotiationCommand, String jsonCommand) {
        ActionLog actionLog = new ActionLog();
        if (null == negotiationCommand.getFromNegotiationPlayerId()) {
            actionLog.setUserType(-1);
        } else {
            BaseRoom room = roomService.findOne(negotiationCommand.getRoomId());
            for (NegotiationPlayer player : room.getNegotiationPlayers()) {
                if (player.getNegotiationPlayerId().equals(negotiationCommand.getFromNegotiationPlayerId())) {
                    actionLog.setUserType(player.getNegotiationPlayerType());
                    actionLog.setUserId(negotiationCommand.getFromNegotiationPlayerId());
                    break;
                }
            }
        }
        // 操作命令
        actionLog.setAction(jsonCommand);
        Date now = new Date();
        actionLog.setCreateTime(now);
        actionLog.setModifyTime(now);

        actionLogService.add(actionLog);
    }

    private void saveNegotiationResult(BaseRoom room, NegotiationPlayer player, Map<String, Object> data) throws JsonProcessingException {
        //保存游戏结果
        ObjectMapper mapper = new ObjectMapper();
        String result = mapper.writeValueAsString(data);
        NegotiationResult negotiationResult = createNegotiationResult(room, player, result);
        negotiationResultService.add(negotiationResult);
    }
}
